/*Constructs a proxy for remote method invocation of a class*/
package Phase1;

import java.lang.reflect.*;
import java.io.FileWriter;
import java.io.File;
import java.io.IOException;
/**
 * create a proxy class for given class
 * @author 
 *
 */
public class ProxyConstructor
{

	private static Class<?> proxyClass;
	private static String result;
	private static Method[] methods;
	/**
	 * constructor
	 */
	private ProxyConstructor()
	{
	}
	/**
	 * 
	 * @param className
	 * the class to be processed
	 * @throws ClassNotFoundException
	 */
	public static void createProxy(String className) throws ClassNotFoundException
	{
		proxyClass = Class.forName(className);
		String classPackage = proxyClass.getPackage().toString();
		
		result = "";
		
		result = "//////////////////////////////////////////\n";
		result += "// Proxy generated class\n";
		result += "//////////////////////////////////////////\n\n";

		result += classPackage + ";\n\n";
		if (!proxyClass.getPackage().toString().contains("Phase1"))
		{
			result += "import Phase1.Command;\n"
				+ "import Phase1.Client;\n"
				+ "import Phase1.GlobalID;\n";

		}
		result += "import java.io.IOException;\n\n";
		
		result += Modifier.toString(proxyClass.getModifiers());
		result += " class " + simpleName(proxyClass.getName()) 
						+ "Proxy\n\textends ";
		result += proxyClass.getName();
		
		result += "\n{\n\n" + "\tprivate static final long serialVersionUID = 1L;\n";
		result += "\tprivate GlobalID globalID;\n\n";
		result += printConstructors();
		
		// print methods
		
		result += printMethods();		
		
		result += "\n}\n";
		
		printToFile(result);
	}
	/**
	 * processing given name, and trim off the part before '.'
	 * @param name
	 * the name to be trimed
	 * @return
	 * the part after '.'
	 */
	private static String simpleName(String name)
	{
		int index = name.lastIndexOf('.') + 1;
		String value = "";
		if(index > 0) 
		{
			value = name.substring(index);
		}

		return value;

	}
	/**
	 * create proxyclass's constructor
	 * @return
	 * proxyclass's constructor
	 */
	private static String printConstructors()
	{
		String value = "";

		value += "\tpublic ";
		value += simpleName(proxyClass.getName()) 
				+ "Proxy(GlobalID globalID";
		value += ")\n\t{" + "\n\t\tthis.globalID = globalID;"
				+ "\n\t}\n\n";
		return value;
	}
	/**
	 * create proxyclass's methods
	 * @return
	 * proxyclass's methods
	 */
	private static String printMethods()
	{
		String value = "";
		
		methods = proxyClass.getDeclaredMethods();
		
		for (Method method : methods)
		{
			value += "\t" + Modifier.toString(method.getModifiers());
			value += " " + method.getReturnType().getName();
			value += " " + method.getName();
			
			value += "(" + printFullParameters(method.getParameterTypes()) + ")\n\t{";
			
			value += "\n\t\tCommand command = new Command(globalID, \"" 
				 + method.getName() + "\", ";
			
			
			// prints parameter type Array
			value += "\n\t\t\t\t\t\t\t\tnew Class[]{";
			if (method.getParameterTypes().length > 0)
			{
				int i = 0;
				for (Class<?> paramType : method.getParameterTypes())
				{
					value += (i++ != 0 ? ", " : "") +  paramType.getName() + ".class";
				}
			}
			
			value += "}, ";
			
			value += "\n\t\t\t\t\t\t\t\tnew Object[]{";
			value += printParameterList(method.getParameterTypes().length);
			
			value += "}, ";
			
			if (!method.getReturnType().getName().equals("void")) 
			{
				value += "true";
			} 
			else 
			{
				value += "false";
			}			
			
			//ProxyConstructor.class.getMethod("bob", new Class[] = {})
				
			value += ");";
			
			value += "\n\n\t\t";
			
			value += "try\n\t\t{\n\t\t\t";
			
			if (!method.getReturnType().getName().equals("void"))
			{
				value += "return (" + getPrimitiveName(method.getReturnType()) + ") "
						+ "Client.sendData";	
			} 
			else 
			{
				value += "Client.voidSendData";
			}
			value += "(globalID, command);";
			
			value += "\n\t\t}\n";
			
			value += printCatchBlock();
			
			if (!method.getReturnType().getName().equals("void")) 
			{
				value += "\n\t\treturn (" 
							+ getPrimitiveName(method.getReturnType()) + ") null;";
			}
			
			// ending bracket for method
			value += "\n\t}\n\n";					
		}
		
		
		return value;		
	}
	
	/**
	 * create exception catching block
	 * @return
	 * statement of catching block
	 */
	private static String printCatchBlock()
	{
		String value = "";
		
		value += "\n\t\tcatch (IOException e)\n\t\t{\n\t\t\t" 
					+ "e.printStackTrace();\n\t\t}" 
					+ "\n\t\tcatch (ClassNotFoundException e)\n\t\t{\n\t\t\t"
					+ "e.printStackTrace();\n\t\t}\n";
		
		return value;		
	}
	
	/**
	 * create parameters of method
	 * @param paramTypes
	 * type of parameters
	 * @return
	 * all parameter with paired type and name.
	 */
	private static String printFullParameters(Class<?>[] paramTypes)
	{
		String value = "";
		
		if(paramTypes.length > 0)
		{
			value += getPrimitiveName(paramTypes[0]) + " p0";
		}
		
		for(int p = 1; p < paramTypes.length; p++)
		{
			value += ", " + getPrimitiveName(paramTypes[p]) + " p" + p;
		}
		
		return value;
	}
	/**
	 * create a list of parameters
	 * @param length
	 * number of parameters
	 * @return
	 * a list of parameters in the form of p0,p1,p2....plength.
	 */
	private static String printParameterList(int length)
	{
		String value = "";
		
		for (int p = 0; p < length; p++)
		{
			if(p == 0) 
			{
				value += "p" + p;
			} 
			else 
			{
				value += ", p" + p;
			}
		}
		
		return value;
	}
	/**
	 * return a class name of given primitive
	 * @param primitive
	 * given primitive
	 * @return
	 * string of the class name of the primitive
	 */
	private static String getPrimitiveName(Class<?> primitive)
	{
		String value = "";
		 
		if (primitive.getName().equals("byte")) 
		{
			value = "Byte";
		} 
		else if (primitive.getName().equals("short")) 
		{
			value = "Short";
		} 
		else if (primitive.getName().equals("int")) 
		{
			value = "Integer";
		} 
		else if (primitive.getName().equals("long")) 
		{
			value = "Long";
		} 
		else if (primitive.getName().equals("float")) 
		{
			value = "Float";
		} 
		else if (primitive.getName().equals("double")) 
		{
			value = "Double";
		} 
		else if (primitive.getName().equals("boolean")) 
		{
			value = "Boolean";
		} 
		else if (primitive.getName().equals("char")) 
		{
			value = "Character";
		} 
		else 
		{
			value = primitive.getName();
		}		
		
		return value;	
	}
	/**
	 * write proxyclass to a .java file.
	 * @param value
	 * the content of proxyclass.
	 * @return
	 * true: successfuly writed. false: failed to create new java file.
	 */
	private static boolean printToFile(String value)
	{
		boolean success = false;
		
		try
		{
			String fileName = proxyClass.getSimpleName() + "Proxy.java";
			FileWriter writer = new FileWriter(new File(fileName));
			writer.write(value);
			writer.close();	
			success = true;
		}
		catch (IOException e)
		{
			System.out.println("Unable to write file!");
			e.printStackTrace();
		}
		
		return success;
	}

}
